package de.codi.structural;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataRange;
import org.semanticweb.owlapi.model.OWLDataSomeValuesFrom;
import org.semanticweb.owlapi.model.OWLObjectComplementOf;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
import org.semanticweb.owlapi.reasoner.OWLReasonerException;

import de.codi.exception.CodiException;
import de.codi.read.StoredOntology;


public class GroundValueGenerator
{
    private StoredOntology ontology;


    public GroundValueGenerator(StoredOntology ontology)
    {
        this.ontology = ontology;
    }


    public void addGroundValues(List<String> groundPredicates, String predicate) throws CodiException
    {

        if(predicate.startsWith(StructuralConstants.CLASS)) {
            addGroundPredicate(groundPredicates, predicate, this.getClasses());
        } else if(predicate.startsWith(StructuralConstants.PROP)) {
            addGroundPredicate(groundPredicates, predicate, this.getProperties());
        } else if(predicate.startsWith(StructuralConstants.DISJOINT)) {
            addGroundPredicate(groundPredicates, predicate, this.getDisjoint());
        } else if(predicate.startsWith(StructuralConstants.SUBSUMES)) {
            addGroundPredicate(groundPredicates, predicate, this.getSubsumes());
        } else if(predicate.startsWith(StructuralConstants.DOMAINDIS)) {
            addGroundPredicate(groundPredicates, predicate, this.getDomaindis());
        } else if(predicate.startsWith(StructuralConstants.DOMAINSUB)) {
            addGroundPredicate(groundPredicates, predicate, this.getDomainsub());
        } else if(predicate.startsWith(StructuralConstants.DOMAINSUP)) {
            addGroundPredicate(groundPredicates, predicate, this.getDomainsup());
        } else if(predicate.startsWith(StructuralConstants.RANGEDIS)) {
            addGroundPredicate(groundPredicates, predicate, this.getRangedis());
        } else if(predicate.startsWith(StructuralConstants.RANGESUB)) {
            addGroundPredicate(groundPredicates, predicate, this.getRangesub());
        } else if(predicate.startsWith(StructuralConstants.RANGESUP)) {
            addGroundPredicate(groundPredicates, predicate, this.getRangesup());
        }

    }


    private void addGroundPredicate(List<String> groundPredicates, String predicate, ArrayList<String[]> groundValues)
    {
        for(String[] groundValue : groundValues) {
            StringBuilder sb = new StringBuilder(predicate);
            sb.append("(");
            for(int i = 0; i < groundValue.length; i++) {
                sb.append("\"" + groundValue[i] + "\"");
                if(i < groundValue.length - 1) {
                    sb.append(", ");
                }
            }
            sb.append(")");
            groundPredicates.add(sb.toString());
        }

    }


    private ArrayList<String[]> getClasses()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        for(OWLClass concept : ontology.classes) {
            resultList.add(new String[] { concept.toString() });
        }
        resultList.add(new String[] { ontology.THING.toString() });
        return resultList;
    }


    private ArrayList<String[]> getProperties()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        for(OWLObjectProperty prop : ontology.objectProperties) {
            resultList.add(new String[] { prop.toString() });
        }
        for(OWLDataProperty prop : ontology.dataProperties) {
            resultList.add(new String[] { prop.toString() });
        }
        return resultList;
    }


    private ArrayList<String[]> getSubsumes()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;
        for(OWLClass concept1 : ontology.classes) {
            // Everything is subsumed by itself
            String[] result = new String[] { concept1.toString(), concept1.toString() };
            resultList.add(result);

            // Contains all Anchestor Classes (but not the "same" class)
            Set<OWLClass> superClasses = ontology.reasoner.getSubClasses(concept1, false).getFlattened();

            for(OWLClass concept2 : superClasses) {
                if(!concept2.equals(ontology.NOTHING) && !concept2.equals(ontology.THING)) {
                    result = new String[] { concept2.toString(), concept1.toString() };
                    resultList.add(result);
                    counter = counter + 1;
                }
            }
        }
        // System.out.println("Subsume Relations found: " + counter);
        return resultList;
    }


    private ArrayList<String[]> getDisjoint()
    {
        HashSet<OWLClass> classes = ontology.classes;
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;
        for(OWLClass concept1 : classes) {
            try {
                OWLObjectComplementOf complement = ontology.factory.getOWLObjectComplementOf(concept1);

                Set<OWLClass> disjointClasses = ontology.reasoner.getSubClasses(complement, false).getFlattened();
                for(OWLClass c : ontology.reasoner.getEquivalentClasses(complement)) {
                    disjointClasses.add(c);
                }
                for(OWLClass c : disjointClasses) {
                    if(!c.equals(ontology.NOTHING) && !c.equals(ontology.THING)) {
                        String[] result = new String[2];
                        result[0] = c.toString();
                        result[1] = concept1.toString();
                        resultList.add(result);
                        // System.out.println("Disjoint are : "+c.toString() + " " + concept1.toString());
                        counter = counter + 1;
                    }
                }
            } catch(StackOverflowError t) {
                // Due to a bug of pellet, we have to catch the stack overflow here
            }
        }
        // System.out.println("Disjoint Relations found: " + counter);
        return resultList;
    }


    /**
     * Returns all existing domain restrictions for object and datatype properties
     * 
     * 
     * @throws OWLReasonerException
     */

    private ArrayList<String[]> getDomainsub()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;

        // Object Properties
        for(OWLObjectProperty property : ontology.objectProperties) {
            try {
                Set<OWLClass> domainClasses = ontology.reasoner.getObjectPropertyDomains(property, false).getFlattened();

                for(OWLClass concept : domainClasses) {
                    if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                        String[] result = new String[2];
                        result[0] = property.toString();
                        result[1] = concept.toString();
                        // System.out.println("testiiiii: "+ result[0] + result[1]);
                        resultList.add(result);
                        counter = counter + 1;
                    }
                }
            } catch(StackOverflowError e) {
                // due to an error of pellet
            }
        }

        // Datatype Properties
        for(OWLDataProperty property : ontology.dataProperties) {
            try {
                Set<OWLClass> domainClasses = ontology.reasoner.getDataPropertyDomains(property, false).getFlattened();

                for(OWLClass concept : domainClasses) {
                    if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                        String[] result = new String[2];
                        result[0] = property.toString();
                        result[1] = concept.toString();
                        // System.out.println("testiiiii: "+ result[0] + result[1]);
                        resultList.add(result);
                        counter = counter + 1;
                    }
                }

            } catch(StackOverflowError e) {
                // due to an error of Pellet
            }
        }
        // System.out.println("Domainsub Relations found: " + counter);
        return resultList;
    }


    /**
     * Returns all existing range restrictions for object properties.
     * 
     * @throws OWLReasonerException
     */

    private ArrayList<String[]> getRangesub()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;

        // Object Properties
        for(OWLObjectProperty property : ontology.objectProperties) {
            Set<OWLClass> domainClasses = ontology.reasoner.getObjectPropertyRanges(property, false).getFlattened();

            for(OWLClass concept : domainClasses) {
                if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                    String[] result = new String[2];
                    result[0] = property.toString();
                    result[1] = concept.toString();
                    resultList.add(result);
                    counter = counter + 1;
                }
            }
        }

        // No Range for datatype Properties.

        // System.out.println("Rangesub Relations found: " + counter);
        return resultList;
    }


    /**
     * This class returns all pair of ids of classes c1 and c2
     * when the following is entailed in the ontology:
     * 
     * SubClassOf(c1 ObjectSomeValuesFrom(someName c2))
     * 
     * 
     * @return
     * @throws OWLReasonerException
     * 
     *             public ArrayList<String[]> getSomeValuesFromClasses() throws OWLReasonerException{
     *             ArrayList<String[]> resultList = new ArrayList<String[]>();
     * 
     *             for(OWLClass sourceConcept: this.classes){
     *             for(OWLObjectProperty property: this.objectProperties){
     *             OWLObjectSomeRestriction someRestriction = factory.getOWLObjectSomeRestriction(property, sourceConcept);
     *             Set<OWLClass> superClasses = OWLReasonerAdapter.flattenSetOfSets(
     *             reasoner.getSubClasses(someRestriction));
     *             for(OWLClass targetConcept : superClasses){
     *             if(!targetConcept.toString().equals("Nothing")){
     *             String[] result = new String[2];
     *             result[0]=sourceConcept.toString();
     *             result[1]=targetConcept.toString();
     *             resultList.add(result);
     *             //System.out.println(sourceConcept.toString() + " " + targetConcept.toString());
     *             }
     *             }
     *             }
     *             }
     *             return resultList;
     *             }
     */

    /**
     * Returns all existing domain restrictions:
     * 
     * SubClassOf(ObjectSomeValuesFrom(P1 owl:Thing) ?C);
     * 
     * or all descendants from
     * 
     * ObjectPropertyDomain( ?P ?C )
     * 
     * @throws OWLReasonerException
     */

    private ArrayList<String[]> getDomainsup()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;
        // Object Properties
        for(OWLObjectProperty property : ontology.objectProperties) {
            try {
                OWLObjectSomeValuesFrom osr = ontology.factory.getOWLObjectSomeValuesFrom(property, ontology.THING);

                // Add all Descendants
                Set<OWLClass> subClasses = ontology.reasoner.getSubClasses(osr, false).getFlattened();
                // ...and all equivalent classes
                subClasses.addAll(ontology.reasoner.getEquivalentClasses(osr).getEntities());

                for(OWLClass concept : subClasses) {
                    if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                        String[] result = new String[] { property.toString(), concept.toString() };
                        resultList.add(result);
                        counter = counter + 1;
                    }

                }
            } catch(StackOverflowError e) {
                // catched due to an error in pellet
            }
        }

        // TODO make this work!!!
        /*
         * // Datatype Properties
         * for(OWLDataProperty property : this.dataProperties){
         * Set<OWLDataRange> ranges = ontology.ont.getDataPropertyRangeAxioms()reasoner.getRanges(property);
         * for(OWLDataRange range: ranges){
         * try{
         * OWLDataSomeValuesFrom osr = ontology.factory.getOWLDataSomeValuesFrom(property, range);
         * // Add all Descendants
         * Set<OWLClass> superClasses =
         * ontology.reasoner.getSubClasses(osr,false).getFlattened();
         * // ...and all equivalent classes
         * superClasses.addAll(ontology.reasoner.getEquivalentClasses(osr).getEntities());
         * for(OWLClass concept: superClasses){
         * if(!concept.equals(ontology.NOTHING)&&!concept.equals(ontology.THING)){
         * String[] result = new String[]{
         * property.toString(),
         * concept.toString()
         * };
         * resultList.add(result);
         * counter = counter+1;
         * }
         * }
         * }catch(StackOverflowError e){
         * // catched due to an error in pellet
         * }
         * }
         * }
         */
        // System.out.println("Domainsup Relations found: " + counter);
        return resultList;
    }


    /**
     * Returns all existing domain restrictions:
     * 
     * SubClassOf(ObjectSomeValuesFrom(InverseOf(P1) owl:Thing) ?C);
     * 
     * or all descendants from
     * 
     * ObjectPropertyDomain(InverseOf(?P) ?C )
     * 
     * @throws OWLReasonerException
     */

    private ArrayList<String[]> getRangesup()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;
        for(OWLObjectProperty property : ontology.objectProperties) {
            try {
                // Simply generate the Some Restriction with the INVERSE Object property

                OWLObjectSomeValuesFrom osr = ontology.factory.getOWLObjectSomeValuesFrom(property.getInverseProperty(), ontology.THING);

                // Add all Descendants
                Set<OWLClass> superClasses = ontology.reasoner.getSuperClasses(osr, false).getFlattened();
                // ...and all equivalent classes
                superClasses.addAll(ontology.reasoner.getEquivalentClasses(osr).getEntities());

                for(OWLClass concept : superClasses) {
                    if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                        String[] result = new String[] { property.toString(), concept.toString() };
                        resultList.add(result);
                        counter = counter + 1;
                    }

                }
            } catch(StackOverflowError e) {
                // catched due to an error in pellet
            }
        }
        // No Range for datatype Properties
        // System.out.println("Rangesup Relations found: " + counter);
        return resultList;
    }


    /**
     * Returns all existing domain restrictions:
     * 
     * DisjointClasses(ObjectSomeValuesFrom(P1 owl:Thing) ?C);
     * 
     * or all disjoint classes from
     * 
     * ObjectPropertyDomain(?P ?C )
     * 
     * @throws OWLReasonerException
     */

    private ArrayList<String[]> getDomaindis()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;
        for(OWLObjectProperty property : ontology.objectProperties) {
            try {
                OWLObjectSomeValuesFrom osr = ontology.factory.getOWLObjectSomeValuesFrom(property, ontology.THING);
                OWLObjectComplementOf complement = ontology.factory.getOWLObjectComplementOf(osr);

                // Add all Disjoint Classes
                Set<OWLClass> disjointClasses = ontology.reasoner.getSubClasses(complement, false).getFlattened();
                for(OWLClass c : ontology.reasoner.getEquivalentClasses(complement)) {
                    disjointClasses.add(c);
                }
                for(OWLClass concept : disjointClasses) {
                    if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                        String[] result = new String[] { property.toString(), concept.toString() };
                        resultList.add(result);
                        counter = counter + 1;
                    }

                }
            } catch(StackOverflowError e) {
                // due to a bug of pellet, we have to catch the error here
            }
        }
        for(OWLDataProperty property : ontology.dataProperties) {
            Set<OWLDataRange> ranges = property.getRanges(ontology.ont);
            for(OWLDataRange range : ranges) {
                try {
                    OWLDataSomeValuesFrom osr = ontology.factory.getOWLDataSomeValuesFrom(property, range);
                    OWLObjectComplementOf complement = ontology.factory.getOWLObjectComplementOf(osr);

                    // Add all Disjoint Classes
                    Set<OWLClass> superClasses = ontology.reasoner.getSubClasses(complement, false).getFlattened();
                    for(OWLClass c : ontology.reasoner.getEquivalentClasses(complement)) {
                        superClasses.add(c);
                    }
                    for(OWLClass concept : superClasses) {
                        if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                            String[] result = new String[] { property.toString(), concept.toString() };
                            resultList.add(result);
                            counter = counter + 1;
                        }

                    }
                } catch(StackOverflowError e) {
                    // due to a bug of pellet, we have to catch this here...
                }
            }
        }
        // System.out.println("Domaindis Relations found: " + counter);
        return resultList;
    }


    /**
     * Returns all existing range restrictions:
     * 
     * DisjointClasses(ObjectSomeValuesFrom(InverseOf(P1) owl:Thing) ?C);
     * 
     * or all disjoint classes from
     * 
     * ObjectPropertyDomain(InverseOf(?P) ?C )
     * 
     * @throws OWLReasonerException
     */
    private ArrayList<String[]> getRangedis()
    {
        ArrayList<String[]> resultList = new ArrayList<String[]>();
        int counter = 0;
        for(OWLObjectProperty property : ontology.objectProperties) {
            try {
                OWLObjectSomeValuesFrom osr = ontology.factory.getOWLObjectSomeValuesFrom(property.getInverseProperty(), ontology.THING);
                OWLObjectComplementOf complement = ontology.factory.getOWLObjectComplementOf(osr);
                // System.out.println("property: " + property);
                Set<OWLClass> superClasses = ontology.reasoner.getSubClasses(complement, false).getFlattened();
                for(OWLClass c : ontology.reasoner.getEquivalentClasses(complement)) {
                    superClasses.add(c);
                }

                for(OWLClass concept : superClasses) {
                    if(!concept.equals(ontology.NOTHING) && !concept.equals(ontology.THING)) {
                        String[] result = new String[] { property.toString(), concept.toString() };
                        resultList.add(result);
                        counter = counter + 1;
                    }

                }
            } catch(StackOverflowError e) {
                // due to a bug of pellet, we have to catch this here.
            }
            // No range of Datatype-Properties (the Range is DataSomeRestrictions, e.g. Integer, String, etc.)
        }
        // System.out.println("Rangedis Relations found: " + counter);
        return resultList;
    }

}
